using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace Framework
{
    public static class StrUtil
    {
        #region DefaultValue
        /// <summary>
        /// Return <code>""</code> if <code>v</code> is empty.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <returns>String</returns>
        public static string DefaultValue(string v)
        {
            return ((v == null || v.Trim().Length == 0) ? String.Empty : v);
        }
        /// <summary>
        /// Return <code>defaultVal</code> if <code>v</code> is empty.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <param name="defaultVal">The default val.</param>
        /// <returns>String</returns>
        public static string DefaultValue(string v, string defaultVal)
        {
            return ((v == null || v.Trim().Length == 0) ? defaultVal : v);
        }
        #endregion

        #region DefaultValueToLower
        /// <summary>
        /// Return <code>""</code> if <code>v</code> is empty.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <returns>String</returns>
        public static string DefaultValueToLower(string v)
        {
            return ((v == null || v.Trim().Length == 0) ? String.Empty : v.ToLower());
        }
        /// <summary>
        /// Return <code>defaultVal</code> if <code>v</code> is empty.
        /// </summary>
        /// <param name="v">The v.</param>
        /// <param name="defaultVal">The default val.</param>
        /// <returns>String</returns>
        public static string DefaultValueToLower(string v, string defaultVal)
        {
            return ((v == null || v.Trim().Length == 0) ? (defaultVal == null ? String.Empty : defaultVal.ToLower()) : v.ToLower());
        }
        #endregion

        #region NullTo
        /// <summary>
        /// Nulls to.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static string NullTo(object value)
        {
            return NullTo(value, string.Empty);
        }

        /// <summary>
        /// Nulls to.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultForNull">The default for null.</param>
        /// <returns></returns>
        public static string NullTo(object value, string defaultForNull)
        {
            return value == null ? defaultForNull : value.ToString();
        }
        #endregion

        #region OldCode
        //#region Split
        //// May want to look into a SplitStringPair and SplitStringTriplet

        ///// <summary>
        ///// Split a string into a string[], check 1st to see if the string is empty and if 
        ///// it is then return an zero length array, string[0]. By default each element is trimmed of whitespace.
        ///// <br /><br />
        ///// Defaults to spliting comma delimited strings
        ///// </summary>
        ///// <param name="v">String to split</param>
        ///// <returns>string[]</returns>
        //public static string[] Split(string v)
        //{
        //    return Split(true, v, ',');
        //}

        ///// <summary>
        ///// Split a string into a string[], check 1st to see if the string is empty and if 
        ///// it is then return an zero length array, string[0]. By default each element is trimmed of whitespace.
        ///// </summary>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <returns>string[]</returns>
        //public static string[] Split(string v, params char[] seperator)
        //{
        //    return Split(true, v, seperator);
        //}

        ///// <summary>
        ///// Split a string into a string[], check 1st to see if the string is empty and if 
        ///// it is then return an zero length array, string[0].
        ///// </summary>
        ///// <param name="trim">If true, each element is trimmed of whitespace.</param>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <returns>string[]</returns>
        //public static string[] Split(bool trim, string v, params char[] seperator)
        //{
        //    if (Is.EmptyString(v))
        //    {
        //        return new string[0];
        //    }
        //    string[] result = v.Split(seperator);

        //    if (trim)
        //    {
        //        for (int i = 0; i < result.Length; i++)
        //        {
        //            result[i] = result[i].Trim();
        //        }
        //    }
        //    return result;
        //}
        //#endregion

        //#region SplitAsList
        ///// <summary>
        ///// Split a string into a List&lt;string&gt;, check 1st to see if the string is empty and if 
        ///// it is then return an empty List. By default each element is trimmed of whitespace.
        ///// <br /><br />
        ///// Defaults to spliting comma delimited strings
        ///// </summary>
        ///// <param name="v">String to split</param>
        ///// <returns>List&lt;string&gt;</returns>
        //public static List<string> SplitAsList(string v)
        //{
        //    return SplitAsList(true, v, ',');
        //}

        ///// <summary>
        ///// Split a string into a List&lt;string&gt;, check 1st to see if the string is empty and if 
        ///// it is then return an empty List. By default each element is trimmed of whitespace.
        ///// </summary>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <returns>List&lt;string&gt;</returns>
        //public static List<string> SplitAsList(string v, params char[] seperator)
        //{
        //    return SplitAsList(true, v, seperator);
        //}

        ///// <summary>
        ///// Split a string into a List&lt;string&gt;, check 1st to see if the string is empty and if 
        ///// it is then return an empty List.
        ///// </summary>
        ///// <param name="trim">If true, each element is trimmed of whitespace.</param>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <returns>List&lt;string&gt;</returns>
        //public static List<string> SplitAsList(bool trim, string v, params char[] seperator)
        //{
        //    if (Is.EmptyString(v))
        //    {
        //        return new List<string>();
        //    }

        //    var result = v.Split(seperator);

        //    if (trim)
        //    {
        //        for (var i = 0; i < result.Length; i++)
        //        {
        //            result[i] = result[i].Trim();
        //        }
        //    }
        //    return new List<string>(result);
        //}

        ///// <summary>
        ///// Split a string into a List&lt;string&gt;, check 1st to see if the string is empty and if 
        ///// it is then return an empty List. By default each element is trimmed of whitespace.
        ///// </summary>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">String seperator to split on</param>
        ///// <returns>List&lt;string&gt;</returns>
        //public static List<string> SplitAsList(string v, params string[] seperator)
        //{
        //    return SplitAsList(true, v, seperator);
        //}

        ///// <summary>
        ///// Split a string into a List&lt;string&gt;, check 1st to see if the string is empty and if 
        ///// it is then return an empty List.
        ///// </summary>
        ///// <param name="trim">If true, each element is trimmed of whitespace.</param>
        ///// <param name="v">String to split</param>
        ///// <param name="seperator">String seperator to split on</param>
        ///// <returns>List&lt;string&gt;</returns>
        //public static List<string> SplitAsList(bool trim, string v, params string[] seperator)
        //{
        //    if (Is.EmptyString(v))
        //    {
        //        return new List<string>();
        //    }

        //    string[] result = v.Split(seperator, StringSplitOptions.None);

        //    if (trim)
        //    {
        //        for (int i = 0; i < result.Length; i++)
        //        {
        //            result[i] = result[i].Trim();
        //        }
        //    }
        //    return new List<string>(result);
        //}
        //#endregion

        //#region Combine
        ///// <summary>
        ///// Combines a string[], using a delimiter. Null strings are treated as String.Empty.
        ///// </summary>
        ///// <param name="values">Combine an array of strings using a delimiter to seperate</param>
        ///// <returns>string</returns>
        //public static string Combine(string[] values)
        //{
        //    return Combine(values, ",", false);
        //}

        //public static string Combine(string[] values, string seperator)
        //{
        //    return Combine(values, seperator, false);
        //}

        ///// <summary>
        ///// Combines a string[], using a delimiter. Null strings are treated as String.Empty.
        ///// </summary>
        ///// <param name="values">Combine an array of strings using a delimiter to seperate</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <param name="suppressEmptyStrings"></param>
        ///// <returns>string</returns>
        //public static string Combine(string[] values, string seperator, bool suppressEmptyStrings)
        //{
        //    if (Is.EmptyArray(values))
        //    {
        //        return String.Empty;
        //    }

        //    if (suppressEmptyStrings)
        //    {
        //        values = ArrayUtil.FilterEmptyStrings(values);

        //        if (Is.EmptyArray(values))
        //        {
        //            return String.Empty;
        //        }
        //    }

        //    StringBuilder result;
        //    using (TextBuilder buffer = TextBuilder.Instance(out result, seperator))
        //    {

        //        for (int i = 0; i < values.Length; i++)
        //        {
        //            if (i == values.Length - 1)
        //            {
        //                buffer.Write(StrUtil.DefaultValue(values[i]));
        //            }
        //            else
        //            {
        //                buffer.WriteLine(StrUtil.DefaultValue(values[i]));
        //            }
        //        }
        //    }

        //    return result.ToString();
        //}

        ///// <summary>
        ///// Combines a List<string>, using a delimiter. Null strings are treated as String.Empty.
        ///// </summary>
        ///// <param name="values">Combine an array of strings using a delimiter to seperate</param>
        ///// <returns>string</returns>
        //public static string Combine(List<string> values)
        //{
        //    return Combine(values, ",", false);
        //}

        //public static string Combine(List<string> values, string seperator)
        //{
        //    return Combine(values, seperator, false);
        //}

        ///// <summary>
        ///// Combines a List<string>, using a delimiter. Null strings are treated as String.Empty.
        ///// </summary>
        ///// <param name="values">Combine an array of strings using a delimiter to seperate</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <param name="suppressEmptyStrings"></param>
        ///// <returns>string</returns>
        //public static string Combine(List<string> values, string seperator, bool suppressEmptyStrings)
        //{
        //    return Combine(values.ToArray(), seperator, suppressEmptyStrings);
        //}

        ///// <summary>
        ///// Combines a List<int>, using a comma delimiter.
        ///// </summary>
        ///// <param name="values">Combine an array of ints using a delimiter to seperate</param>
        ///// <returns>string</returns>
        //public static string Combine(List<int> values)
        //{
        //    return Combine(values, ",", false);
        //}

        ///// <summary>
        ///// Combines a List<int>, using a delimiter.
        ///// </summary>
        ///// <param name="values">The values.</param>
        ///// <param name="seperator">The seperator.</param>
        ///// <returns></returns>
        //public static string Combine(List<int> values, string seperator)
        //{
        //    return Combine(values, seperator, false);
        //}

        ///// <summary>
        ///// Combines a List<int>, using a delimiter. Null strings are treated as String.Empty.
        ///// </summary>
        ///// <param name="values">Combine an array of strings using a delimiter to seperate</param>
        ///// <param name="seperator">seperator to split on</param>
        ///// <param name="suppressEmptyStrings"></param>
        ///// <returns>string</returns>
        //public static string Combine(List<int> values, string seperator, bool suppressEmptyStrings)
        //{
        //    if (Is.EmptyArray(values))
        //    {
        //        return Combine(new string[] { }, seperator, suppressEmptyStrings);
        //    }

        //    var stringValues = new string[values.Count];

        //    for (int i = 0; i < values.Count; i++)
        //    {
        //        stringValues[i] = values[i].ToString();
        //    }

        //    return Combine(stringValues, seperator, suppressEmptyStrings);
        //}
        //#endregion

        //#region Join
        ///// <summary>
        ///// Join two strings together with a seperator. Join ensures that there is only one seperator between the two strings
        ///// </summary>
        ///// <param name="stringLhs">String on left hand side</param>
        ///// <param name="stringRhs">String on right hand side</param>
        ///// <param name="seperator">seperator</param>
        ///// <returns>LHS + Seperator + RHS</returns>
        //public static string Join(string stringLhs, string stringRhs, string seperator)
        //{
        //    var s1 = (stringLhs == null ? String.Empty : stringLhs.Trim());
        //    var s2 = (stringRhs == null ? String.Empty : stringRhs.Trim());
        //    var s1HasSeperator = s1.EndsWith(seperator);
        //    var s2HasSeperator = s2.StartsWith(seperator);

        //    var result = new StringBuilder(s1.Length + s2.Length + seperator.Length);

        //    if (s1HasSeperator && s2HasSeperator)
        //    {
        //        result.Append(s1);
        //        result.Append(s2.Substring(seperator.Length));
        //    }
        //    else if (s1HasSeperator || s2HasSeperator)
        //    {
        //        result.Append(s1);
        //        result.Append(s2);
        //    }
        //    else
        //    {
        //        result.Append(s1);
        //        result.Append(seperator);
        //        result.Append(s2);
        //    }

        //    return result.ToString();
        //}
        //#endregion
        #endregion

        #region CsvRecord
        /// <summary>
        /// Builds a valid CSV record from a string[], forces quote markes around each value
        /// </summary>
        /// <param name="values">Values for the CSV record</param>
        /// <returns>string</returns>
        public static string CsvRecord(string[] values)
        {
            return CsvRecord(values, true);
        }

        /// <summary>
        /// Builds a valid CSV record from a string[]
        /// </summary>
        /// <param name="values">Values for the CSV record</param>
        /// <param name="forceQuotes">Should each value always be surrounded by quote marks OR only if value contains comma or quote mark</param>
        /// <returns>string</returns>
        public static string CsvRecord(string[] values, bool forceQuotes)
        {
            var result = new StringWriter(new StringBuilder());

            CsvRecord(result, values, forceQuotes);

            return result.ToString();
        }

        /// <summary>
        /// Builds a valid CSV record from a string[]
        /// </summary>
        /// <param name="writer">Text writer to render a CSV line too</param>
        /// <param name="values">Values for the CSV record</param>
        /// <param name="forceQuotes">Should each value always be surrounded by quote marks OR only if value contains comma or quote mark</param>
        /// <returns>string</returns>
        public static void CsvRecord(TextWriter writer, string[] values, bool forceQuotes)
        {
            for (int i = 0; i < values.Length; i++)
            {
                CsvItem(writer, values[i], forceQuotes);

                writer.Write(i < values.Length - 1 ? ',' : '\n');
            }
        }

        private static void CsvItem(TextWriter writer, string item, bool forceQuotes)
        {
            if (item == null)
            {
                return;
            }

            if (forceQuotes || item.IndexOfAny("\",\x0A\x0D".ToCharArray()) > -1)
            {
                writer.Write("\"");
                writer.Write(item.Replace("\"", "\"\""));
                writer.Write("\"");
            }
            else
            {
                writer.Write(item);
            }
        }
        #endregion

        #region Contains (Case insensitive)
        public static bool Contains(string value, string searchValue)
        {
            return value != null && value.IndexOf(searchValue, FrameworkConstant.DEFAULT_COMPARISON_IGNORECASE) > -1;
            // Use this because string.Contains() cannot be used in a case-insensitive manner
        }

        #endregion

        #region Left
        /// <summary>
        /// Gets the leftmost n characters of a String. If n characters are not
        /// available, or the String is <code>null</code>, the String will be
        /// returned without an exception.
        /// </summary>
        /// <param name="value">The String to get the leftmost characters from</param>
        /// <param name="len">The len.</param>
        /// <returns>The leftmost characters</returns>
        public static string Left(string value, int len)
        {
            if (len < 0 || value == null || value.Length <= len)
            {
                return value;
            }
            return value.Substring(0, len);
        }

        /// <summary>
        /// Gets the leftmost n characters of a String. If n characters are not
        /// available, or the String is <code>null</code>, the String will be
        /// returned without an exception.
        /// </summary>
        /// <param name="value">The String to get the leftmost characters from</param>
        /// <param name="len">The len.</param>
        /// <param name="terminator">The terminator.</param>
        /// <returns>The leftmost characters</returns>
        public static string Left(string value, int len, string terminator)
        {
            if (len < 0 || value == null || terminator == null || len - terminator.Length < 0 || value.Length <= len + terminator.Length)
            {
                return value;
            }
            return value.Substring(0, len - terminator.Length) + terminator;
        }
        #endregion

        #region LeftIndexOf
        /// <summary>
        /// Gets the left most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the left most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <returns>The left most characters</returns>
        public static string LeftIndexOf(string value, string search)
        {
            return LeftIndexOf(value, search, 0, false);
        }

        /// <summary>
        /// Gets the left most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the left most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="includeSearchString">If true will include the search string in the result, if false then the search string is excluded</param>
        /// <returns>The left most characters</returns>
        public static string LeftIndexOf(string value, string search, bool includeSearchString)
        {
            return LeftIndexOf(value, search, 0, includeSearchString);
        }

        /// <summary>
        /// Gets the left most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the left most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="fromIndex">The index to start the search from</param>
        /// <returns>The left most characters</returns>
        public static string LeftIndexOf(string value, string search, int fromIndex)
        {
            return LeftIndexOf(value, search, fromIndex, false);
        }

        /// <summary>
        /// Gets the left most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the left most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="fromIndex">The index to start the search from</param>
        /// <param name="includeSearchString">If true will include the search string in the result, if false then the search string is excluded</param>
        /// <returns>The left most characters</returns>
        public static string LeftIndexOf(string value, string search, int fromIndex, bool includeSearchString)
        {
            if (Is.EmptyString(value) || Is.EmptyString(search))
            {
                return value;
            }
            if (fromIndex > value.Length)
            {
                return value;
            }

            var pos = value.IndexOf(search, fromIndex, StringComparison.Ordinal);

            return pos == -1 
                ? value 
                : value.Substring(0, pos + (includeSearchString ? search.Length : 0));
        }
        #endregion

        #region LeftOf

        /// <summary>
        /// Gets the characters to the left of <var>pos</var>.
        /// </summary>
        /// <param name="value">The String to get the left most characters from</param>
        /// <param name="pos">The position to cut from</param>
        /// <returns>The left most characters</returns>
        public static string LeftOf(string value, int pos)
        {
            if (value == null)
            {
                return null;
            }

            if (pos == -1) return "";
            if (pos > value.Length) return value;

            return Substring(value, 0, pos);
        }
        #endregion

        #region Right
        /// <summary>
        /// Gets the right most n characters of a String. If n characters are not
        /// available, or the String is <code>null</code>, the String will be
        /// returned without an exception.
        /// </summary>
        /// <param name="value">The String to get the right most characters from</param>
        /// <param name="int">The length of the required String</param>
        /// <returns>The right most characters</returns>
        public static string Right(string value, int len)
        {
            if (len < 0 || value == null || value.Length <= len)
            {
                return value;
            }

            return value.Substring(value.Length - len);
        }

        #endregion

        #region RightIndexOf
        /// <summary>
        /// Gets the right most characters starting from the index of <var>search</var>.
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the right most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <returns>The right most characters</returns>
        public static string RightIndexOf(string value, string search)
        {
            return RightIndexOf(value, search, 0, false);
        }

        /// <summary>
        /// Gets the right most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the right most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="includeSearchString">If true will include the search string in the result, if false then the search string is excluded</param>
        /// <returns>The right most characters</returns>
        public static string RightIndexOf(string value, string search, bool includeSearchString)
        {
            return RightIndexOf(value, search, 0, includeSearchString);
        }

        /// <summary>
        /// Gets the right most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the right most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="fromIndex">The index to start the search from</param>
        /// <returns>The right most characters</returns>
        public static string RightIndexOf(string value, string search, int fromIndex)
        {
            return RightIndexOf(value, search, fromIndex, false);
        }

        /// <summary>
        /// Gets the right most characters starting from the index of <var>search</var>
        /// <br/>
        /// If <var>value</var> is <code>null</code>, then <var>value</var> will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the right most characters off</param>
        /// <param name="search">The string to search for</param>
        /// <param name="fromIndex">The index to start the search from</param>
        /// <param name="includeSearchString">If true will include the search string in the result, if false then the search string is excluded</param>
        /// <returns>The right most characters</returns>
        public static string RightIndexOf(string value, string search, int fromIndex, bool includeSearchString)
        {
            if (Is.EmptyString(value) || Is.EmptyString(search) || fromIndex > value.Length)
            {
                return value;
            }

            var pos = value.IndexOf(search, fromIndex);

            return pos == -1 ? value : value.Substring(pos + (includeSearchString ? 0 : search.Length));
        }
        #endregion

        #region RightLastIndexOf
        /// <summary>
        /// Gets the right most characters starting from the last index of <var>search</var>.
        /// <br/>
        /// If the String is <code>null</code>, the String will be returned without an exception.
        /// <br/><br/>
        /// If <var>search</var> is not found then <var>value</var> will be returned without an exception.
        /// </summary>
        /// <param name="value">The string to get the right most characters off</param>
        /// <param name="search">The last occurence of search to start from</param>
        /// <returns>The right most characters</returns>
        public static string RightLastIndexOf(string value, string search)
        {
            if ((value == null) || (search == null))
            {
                return value;
            }

            var pos = value.LastIndexOf(search, StringComparison.Ordinal);

            return pos == -1 ? value : value.Substring(pos + search.Length);
        }
        #endregion

        #region RightOf
        /// <summary>
        /// Gets the characters to the right of <var>pos</var>.
        /// </summary>
        /// <param name="value">The String to get the right most characters from</param>
        /// <param name="pos">The position to cut from</param>
        /// <returns>The right most characters</returns>
        public static string RightOf(string value, int pos)
        {
            if (value == null)
            {
                return null;
            }
            return pos == -1 ? value : Substring(value, pos + 1);
        }
        /// <summary>
        /// Gets the characters to the right of <var>pos</var>.
        /// </summary>
        /// <param name="value">The String to get the right most characters from</param>
        /// <param name="pos">The position to cut from</param>
        /// <param name="length">The length.</param>
        /// <returns>
        /// The right most characters
        /// </returns>
        public static string RightOf(string value, int pos, int length)
        {
            if (value == null)
            {
                return null;
            }
            return pos == -1 ? value : Substring(value, pos + 1, length);
        }
        #endregion

        #region Substring
        /// <summary>
        /// Gets a substring from the specified string avoiding exceptions from invalid start indexes or null strings.
        /// </summary>
        /// <param name="value">The String to get the substring from</param>
        /// <param namestartint">The index of the start of the substring</param>
        /// <returns>substring from start position</returns>
        public static string Substring(string value, int start)
        {
            if (value == null) return String.Empty;

            if (start < 0) start = 0;                   // Start must be 0 or greater
            if (start > value.Length) return String.Empty;         // Start to big

            return value.Substring(start);
        }

        /// <summary>
        /// Gets a substring from the specified string avoiding exceptions from invalid start indexes, lengths or null strings.
        /// </summary>
        /// <param name="value">The String to get the substring from</param>
        /// <param name="start">The index of the start of the substring</param>
        /// <param name="length">The number of characters in the substring. </param>
        /// <returns>substring from start position for <var>length</var> characters</returns>
        public static string Substring(string value, int start, int length)
        {
            if (value == null) return String.Empty;

            if (start < 0)
            {
                start = 0;                     // Start must be 0 or greater
            }
            if (length > value.Length - start)
            {
                length = value.Length - start;
            }

            if (length < 0) return String.Empty;

            return value.Substring(start, length);
        }
        #endregion

        #region StartsWith
        /// <summary>
        /// Similer to String.StartsWith except that it is case-insensitive.
        /// </summary>
        /// <param name="value">The string to test</param>
        /// <param name="startsWith">The string to seek</param>
        /// <returns><b>true</b> if value matches the beginning of this string or is Empty; otherwise <b>false</b></returns>
        public static bool StartsWith(string value, string startsWith)
        {
            if (value == null || startsWith == null || startsWith.Length > value.Length)
            {
                return false;
            }

            // REFACTOR: Would be quicker to test each character in a loop
            return value.ToLower().StartsWith(startsWith.ToLower());
        }
        #endregion

        #region Trim
        public static string Trim(string value)
        {
            if (value == null) return String.Empty;

            return value.Trim();
        }
        public static string TrimStart(string value)
        {
            if (value == null) return String.Empty;

            return value.TrimStart();
        }
        public static string TrimEnd(string value)
        {
            if (value == null) return String.Empty;

            return value.TrimEnd();
        }
        #endregion

        #region EndsWith
        /// <summary>
        /// Similer to String.EndsWith except that it is case-insensitive.
        /// </summary>
        /// <param name="value">The string to test</param>
        /// <param name="endsWith">The string to seek</param>
        /// <returns><b>true</b> if value matches the beginning of this string or is Empty; otherwise <b>false</b></returns>
        public static bool EndsWith(string value, string endsWith)
        {
            if (value == null || endsWith == null || endsWith.Length > value.Length)
            {
                return false;
            }

            return value.EndsWith(endsWith, FrameworkConstant.DEFAULT_COMPARISON_IGNORECASE);
        }
        #endregion

        #region Replace
        /// <summary>
        /// String replacement using desired case-sensitivity.
        /// </summary>
        /// <param name="original">The original.</param>
        /// <param name="searchString">The search string.</param>
        /// <param name="replacementString">The replacement string.</param>
        /// <returns></returns>
        public static string Replace(string original, string searchString, string replacementString)
        {
            return Replace(original, searchString, replacementString, FrameworkConstant.DEFAULT_COMPARISON_IGNORECASE);
        }
    
        /// <summary>
        /// String replacement using desired case-sensitivity.
        /// </summary>
        /// <param name="original">The original.</param>
        /// <param name="searchString">The search string.</param>
        /// <param name="replacementString">The replacement string.</param>
        /// <param name="comparisonType">Type of the comparison.</param>
        /// <returns></returns>
        static public string Replace(string original, string searchString, string replacementString, StringComparison comparisonType)
        {
            if (original == null)
            {
                return null;
            }

            if (String.IsNullOrEmpty(searchString))
            {
                return original;
            }

            var lenPattern = searchString.Length;
            var idxPattern = -1;
            var idxLast = 0;

            var result = new StringBuilder();

            while (true)
            {
                idxPattern = original.IndexOf(searchString, idxPattern + 1, comparisonType);

                if (idxPattern < 0)
                {
                    result.Append(original, idxLast, original.Length - idxLast);

                    break;
                }

                result.Append(original, idxLast, idxPattern - idxLast);
                result.Append(replacementString);

                idxLast = idxPattern + lenPattern;
            }

            return result.ToString();
        }
        #endregion

        public delegate bool ReplaceUnlessPredicate2(string original, string searchString, string replacementString, StringComparison comparisonType, StringBuilder result, int idxLast, int idxPattern, int lenPattern);
        public delegate bool ReplaceUnlessPredicate(string searchString, string beforeSearch, string afterSearch);

        #region ReplaceWhen
        /// <summary>
        /// String replacement using desired case-sensitivity, this is a custom usage pattern with predicate that can skip a replacement if needed
        /// </summary>
        /// <param name="original">The original.</param>
        /// <param name="searchString">The search string.</param>
        /// <param name="replacementString">The replacement string.</param>
        /// <param name="comparisonType">Type of the comparison.</param>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        static public string ReplaceWhen(string original, string searchString, string replacementString, StringComparison comparisonType, ReplaceUnlessPredicate predicate)
        {
            if (original == null)
            {
                return null;
            }

            if (String.IsNullOrEmpty(searchString))
            {
                return original;
            }

            var lenPattern = searchString.Length;
            var idxPattern = -1;
            var idxLast = 0;

            var result = new StringBuilder();

            while (true)
            {
                idxPattern = original.IndexOf(searchString, idxPattern + 1, comparisonType);

                if (idxPattern < 0)
                {
                    result.Append(original, idxLast, original.Length - idxLast);

                    break;
                }

                result.Append(original, idxLast, idxPattern - idxLast);

                var beforeSearch = LeftOf(original, idxPattern);
                var afterSearch = RightOf(original, idxPattern - 1 + lenPattern);
                var valueToAppend = predicate(searchString, beforeSearch, afterSearch)
                                        ? replacementString
                                        : searchString;

                result.Append(valueToAppend);

                idxLast = idxPattern + lenPattern;
            }

            return result.ToString();
        }
        #endregion

        #region Replicate
        public static string Replicate(int len)
        {
            return Replicate(len, ' ');
        }

        public static string Replicate(int len, char c)
        {
            if (len < 1)
            {
                return "";
            }

            var line = new char[len];

            ArrayUtil.FillCharArray(line, c, 0, len);

            return new string(line);
        }

        public static string Replicate(int len, string s)
        {
            char[] data = s.ToCharArray();
            int dataSize = data.Length;
            char[] result = new char[dataSize * len];

            for (int i = 0; i < len; i++)
            {
                ArrayUtil.FillCharArray(result, data, i * dataSize, dataSize);
            }

            return new String(result);
        }
        #endregion

        #region ToLower
        public static string ToLower(string s)
        {
            if (s == null)
            {
                return null;
            }

            return s.ToLower();
        }
        #endregion

        #region Concat
        /// <summary> Concatenates a list of objects as a string. </summary>
        /// <param name="list">The list of objects to concatenate. </param>
        /// <returns> A text representation of the concatenated objects. </returns>
        public static string Concat(IList list)
        {
            var sb = new StringBuilder();

            foreach (var t in list)
            {
                sb.Append(t.ToString());
            }

            return sb.ToString();
        }
        #endregion

        #region PadR
        public static string PadR(string value)
        {
            return PadR(value, 100, ' ');
        }
        public static string PadR(string value, int totalWidth)
        {
            return PadR(value, totalWidth, ' ');
        }
        public static string PadR(string value, int totalWidth, char paddingChar)
        {
            return PadR(value, totalWidth, paddingChar, false);
        }
        public static string PadR(string value, int totalWidth, char paddingChar, bool trimToTotalWidth)
        {
            var result = (value ?? String.Empty);

            // If we are not trimming to the total width then set the width to the string length if it is more then total width
            if (!trimToTotalWidth)
            {
                totalWidth = (totalWidth < result.Length ? result.Length : totalWidth);
            }

            return Left(result.PadRight(totalWidth, paddingChar), totalWidth);
        }
        #endregion

        #region PadL
        public static string PadL(string value)
        {
            return PadL(value, 100, ' ');
        }
        public static string PadL(string value, int totalWidth)
        {
            return PadL(value, totalWidth, ' ');
        }
        public static string PadL(string value, int totalWidth, char paddingChar)
        {
            return PadL(value, totalWidth, paddingChar, false);
        }
        public static string PadL(string value, int totalWidth, char paddingChar, bool trimToTotalWidth)
        {
            var result = (value ?? String.Empty);

            // If we are not trimming to the total width then set the width to the string length if it is more then total width
            if (!trimToTotalWidth)
            {
                totalWidth = (totalWidth < result.Length ? result.Length : totalWidth);
            }

            return Left(result.PadLeft(totalWidth, paddingChar), totalWidth);
        }
        #endregion

        #region PadC
        public static string PadC(string value)
        {
            return PadC(value, 100, ' ');
        }
        public static string PadC(string value, int totalWidth)
        {
            return PadC(value, totalWidth, ' ');
        }
        public static string PadC(string value, int totalWidth, char paddingChar)
        {
            return PadC(value, totalWidth, paddingChar, false);
        }
        public static string PadC(string value, int totalWidth, char paddingChar, bool trimToTotalWidth)
        {
            // Do not trim the string
            string result = (value ?? String.Empty);

            // If we are not trimming to the total width then set the width to the string length if it is more then total width
            if (!trimToTotalWidth)
            {
                totalWidth = (totalWidth < result.Length ? result.Length : totalWidth);
            }

            if (result.Length > totalWidth)
            {
                return result.Substring(0, totalWidth);
            }

            int paddingSize = totalWidth - result.Length;

            int paddingSizeL = paddingSize / 2;
            int paddingSizeR = paddingSize / 2;

            if (paddingSize % 2 == 1)
            {
                if (paddingSizeR >= 0)
                {
                    paddingSizeR++;
                }
            }

            result =
                String.Empty.PadRight(paddingSizeL, paddingChar) +
                result +
                String.Empty.PadRight(paddingSizeR, paddingChar);

            /*
            _.P("'{0}' Size: {1} SizeL: {2} SizeR: {3} Mod: {4} TotalWidth: {5} AddUpWidht: {6} StringWidth {7}",
                new object[] {
                cleanString, 
                paddingSize, 
                paddingSizeL, 
                paddingSizeR, 
                paddingSize % 2, 
                totalWidth, 
                (paddingSizeL+paddingSizeR+cleanString.Length), 
                result.Length});
            */

            return Left(result, totalWidth);
        }
        #endregion

        #region ReplaceRegExProperty
        public static string ReplaceRegExProperty(string applyTo, IDictionary keyValues, RegularExpressionType regExType, string delimLeft, string delimRight)
        {
            if (Is.EmptyString(applyTo))
            {
                return applyTo;
            }

            var result = applyTo;
            var isKeepGoing = true;
            var regEx = RegExUtil.GetRegEx(regExType);
            Match m;
            string propToken;

            while (isKeepGoing)
            {
                m = regEx.Match(result);

                isKeepGoing = m.Success;

                if (m.Success)
                {
                    var propName = m.Groups["prop"].Value;
                    propToken = delimLeft + propName + delimRight;

                    if (keyValues == null)
                    {
                        result = result.Replace(propToken, String.Empty);
                    }
                    else
                    {  // This is only true during initialization
                        var replaceWith = keyValues[propName];

                        if (replaceWith == null || replaceWith.ToString().ToLower().IndexOf(propToken.ToLower(), StringComparison.Ordinal) > -1)
                        {
                            replaceWith = String.Empty;
                        }

                        result = result.Replace(propToken, replaceWith.ToString());
                    }
                }
            }

            return result;
        }

        public static string ReplaceRegExProperty(string applyTo, ITokenReplacer tokenReplacer, RegularExpressionType regExType, string delimLeft, string delimRight)
        {
            if (Is.EmptyString(applyTo))
            {
                return applyTo;
            }

            var processedProperties = new Dictionary<string, int>(FrameworkConstant.DEFAULT_COMPARER_IGNORECASE);

            var result = applyTo;
            var isKeepGoing = true;
            var regEx = RegExUtil.GetRegEx(regExType);
            Match m;

            while (isKeepGoing)
            {
                m = regEx.Match(result);

                isKeepGoing = m.Success;

                if (m.Success)
                {
                    var propName = m.Groups["prop"].Value;
                    var propToken = delimLeft + propName + delimRight;

                    if (processedProperties.ContainsKey(propName))
                    {
                        processedProperties.Set(propName, processedProperties[propName]+1);
                    }
                    else
                    {
                        processedProperties.Add(propName, 1);
                    }

                    if (processedProperties[propName]>5)
                    {// Seems like we are going into recursion
                        _.P("Seems like we are going into recursion: {0}", propName);
                        result = result.Replace(propToken, "******** RECURSION ENCOUNTERED [{0}] *********".FormatWith(propName));
                        continue;
                    }

                    var tokenValues = DelimitedUtil.Split(propName, ':');

                    if (tokenValues.Length == 0)
                    {
                        result = result.Replace(propToken, String.Empty);
                    }
                    else
                    {
                        var tokenKey = tokenValues[0];

                        var tokenHandler = tokenReplacer.FindHandler(tokenKey);

                        var tokenParmeters = GetTokenParameters(tokenValues);

                        result = result.Replace(propToken, tokenHandler(tokenKey, tokenReplacer, tokenParmeters));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Replaces the reg ex property.
        /// </summary>
        /// <param name="tokenValues">The token values.</param>
        /// <returns></returns>
        /// <remarks>
        /// Property Examples
        /// ${Property1}
        /// ${Property2}
        /// ${Property2:Param1=David:Param2=Cruwys}
        /// Rules:
        /// Properties can be replaced by either dictionary values and/or TokenReplacer values.
        /// Properties may optionally have parameters in them.
        /// If a property has 1 or more parameters
        /// Property will be replaced with TokenReplacer value
        /// If a property does not have any parameters
        /// If the Dictionary contains the property name
        /// Property will be replaced with Dictionary value
        /// Else
        /// Property will be replaced with TokenReplacer value
        /// </remarks>
        private static TokenParameters GetTokenParameters(string[] tokenValues)
        {
            var result = new TokenParameters();

            // The 1st Token Value is the name of the tokenHandler, we can ignore that from a parameters perspective
            if (tokenValues.Length > 1)
            {
                // Start from the 2nd parameter
                for (var i = 1; i < tokenValues.Length; i++)
                {
                    var keyValue = DelimitedUtil.Split(tokenValues[i], '=');

                    // If we have a Name=Value combination then add it to the parameters
                    if (keyValue.Length == 2)
                    {
                        if (!result.ContainsKey(keyValue[0]))
                        {
                            result.Add(keyValue[0], WebUtil.UrlDecode(keyValue[1]));
                        }
                    }
                }
            }

            return result;
        }
        #endregion

        #region ReplaceBraceProperty
        public static string ReplaceBraceProperty(string applyTo, params string[] keyValues)
        {
            return ReplaceBraceProperty(applyTo, DictionaryUtil.KeyValueArrayAsDictionary(keyValues));
        }
        public static string ReplaceBracePropertyHashtable(string applyTo, IDictionary keyValues)
        {
            return ReplaceRegExProperty(applyTo, keyValues, RegularExpressionType.MacroPropertyBrace, "${", "}");
        }
        public static string ReplaceBraceProperty(string applyTo, IDictionary<string, string> keyValues)
        {
            IDictionary newKeyValues = DictionaryUtil.GenericDictionaryToHashtable(keyValues);

            return ReplaceRegExProperty(applyTo, newKeyValues, RegularExpressionType.MacroPropertyBrace, "${", "}");
        }
        public static string ReplaceBraceProperty(string applyTo, ITokenReplacer tokenReplacer)
        {
            return ReplaceRegExProperty(applyTo, tokenReplacer, RegularExpressionType.MacroPropertyBrace, "${", "}");
        }
        #endregion

        #region ReplaceSquareProperty
        public static string ReplaceSquareProperty(string applyTo, params string[] keyValues)
        {
            return ReplaceSquareProperty(applyTo, DictionaryUtil.KeyValueArrayAsDictionary(keyValues));
        }
        public static string ReplaceSquarePropertyHashtable(string applyTo, IDictionary keyValues)
        {
            return ReplaceRegExProperty(applyTo, keyValues, RegularExpressionType.MacroPropertySquare, "$[", "]");
        }
        public static string ReplaceSquareProperty(string applyTo, IDictionary<string, string> keyValues)
        {
            IDictionary newKeyValues = DictionaryUtil.GenericDictionaryToHashtable(keyValues);

            return ReplaceRegExProperty(applyTo, newKeyValues, RegularExpressionType.MacroPropertySquare, "$[", "]");
        }
        public static string ReplaceSquareProperty(string applyTo, ITokenReplacer tokenReplacer)
        {
            return ReplaceRegExProperty(applyTo, tokenReplacer, RegularExpressionType.MacroPropertySquare, "$[", "]");
        }
        #endregion

        #region ReplaceRoundProperty
        public static string ReplaceRoundProperty(string applyTo, params string[] keyValues)
        {
            return ReplaceRoundProperty(applyTo, DictionaryUtil.KeyValueArrayAsDictionary(keyValues));
        }
        public static string ReplaceRoundPropertyHashtable(string applyTo, IDictionary keyValues)
        {
            return ReplaceRegExProperty(applyTo, keyValues, RegularExpressionType.MacroPropertyRound, "$(", ")");
        }
        public static string ReplaceRoundProperty(string applyTo, IDictionary<string, string> keyValues)
        {
            IDictionary newKeyValues = DictionaryUtil.GenericDictionaryToHashtable(keyValues);

            return ReplaceRegExProperty(applyTo, newKeyValues, RegularExpressionType.MacroPropertyRound, "$(", ")");
        }
        public static string ReplaceRoundProperty(string applyTo, ITokenReplacer tokenReplacer)
        {
            return ReplaceRegExProperty(applyTo, tokenReplacer, RegularExpressionType.MacroPropertyRound, "$(", ")");
        }
        #endregion

        #region ReplaceHashProperty
        public static string ReplaceHashProperty(string applyTo, params string[] keyValues)
        {
            return ReplaceHashProperty(applyTo, DictionaryUtil.KeyValueArrayAsDictionary(keyValues));
        }
        public static string ReplaceHashPropertyHashtable(string applyTo, IDictionary keyValues)
        {
            return ReplaceRegExProperty(applyTo, keyValues, RegularExpressionType.MacroPropertyHash, "$#", "#");
        }
        public static string ReplaceHashProperty(string applyTo, IDictionary<string, string> keyValues)
        {
            IDictionary newKeyValues = DictionaryUtil.GenericDictionaryToHashtable(keyValues);

            return ReplaceRegExProperty(applyTo, newKeyValues, RegularExpressionType.MacroPropertyHash, "$#", "#");
        }
        public static string ReplaceHashProperty(string applyTo, ITokenReplacer tokenReplacer)
        {
            return ReplaceRegExProperty(applyTo, tokenReplacer, RegularExpressionType.MacroPropertyHash, "$#", "#");
        }
        #endregion
    }
}
